#!/usr/bin/env bash
set -euo pipefail

# 固定版本与下载地址
OPENSSL_VER="3.5.2"
OPENSSH_VER="10.0p2"
OPENSSL_TGZ="openssl-${OPENSSL_VER}.tar.gz"
OPENSSH_TGZ="openssh-${OPENSSH_VER}.tar.gz"
OPENSSL_URL="https://mirrors.aliyun.com/macports/distfiles/openssl3/${OPENSSL_TGZ}"
OPENSSH_URL="https://mirrors.aliyun.com/pub/OpenBSD/OpenSSH/portable/${OPENSSH_TGZ}"

PREFIX="/usr/local"
OPENSSL_CONF_DIR="${PREFIX}/ssl"
BUILD_DIR="/usr/local/src"

RAND_MIN=20000
RAND_MAX=40000

need_root(){ [ "$(id -u)" -eq 0 ] || { echo "请以 root 身份运行。"; exit 1; }; }

detect_pm(){
  if command -v apt-get >/dev/null 2>&1; then PM=apt
  elif command -v dnf >/dev/null 2>&1; then PM=dnf
  elif command -v yum >/dev/null 2>&1; then PM=yum
  elif command -v zypper >/dev/null 2>&1; then PM=zypper
  else echo "未识别包管理器"; exit 1; fi
  echo "检测到包管理器：$PM"
}

sys_update(){
  echo ">>> 更新系统补丁..."
  case "$PM" in
    apt) export DEBIAN_FRONTEND=noninteractive; apt-get update -y; apt-get dist-upgrade -y; apt-get autoremove -y;;
    dnf) dnf -y upgrade --refresh;;
    yum) yum -y update;;
    zypper) zypper --non-interactive refresh; zypper --non-interactive update;;
  esac
}

install_build_deps(){
  echo ">>> 安装构建依赖..."
  case "$PM" in
    apt) apt-get install -y build-essential wget curl tar pkg-config \
          zlib1g-dev libpam0g-dev libselinux1-dev libedit-dev \
          libkrb5-dev ca-certificates autoconf automake libtool \
          bison flex file ;;
    dnf|yum) $PM -y groupinstall "Development Tools" || true
            $PM -y install wget curl tar pkgconfig zlib-devel \
              pam-devel libselinux-devel libedit-devel krb5-devel \
              ca-certificates openssl-devel autoconf automake libtool \
              bison flex file ;;
    zypper) zypper --non-interactive install -y --type pattern devel_C_C++ || true
            zypper --non-interactive install -y wget curl tar pkg-config \
              zlib-devel pam-devel libselinux-devel libedit-devel \
              krb5-devel ca-certificates autoconf automake libtool \
              bison flex file ;;
  esac
}

prepare_dirs(){ mkdir -p "$BUILD_DIR"; cd "$BUILD_DIR"; export PATH="${PREFIX}/bin:${PREFIX}/sbin:${PATH}"; }

download_checked(){
  local url="$1" save="$2"
  echo ">>> 下载 $(basename "$save") ..."
  curl -fSL "$url" -o "$save"
  file "$save" | grep -qi 'gzip compressed data' || { echo "下载文件不是有效的 gzip：$save"; exit 1; }
  echo ">>> 归档预览（前5行）："; tar -tzf "$save" 2>/dev/null | head -n5 || true
}

find_topdir(){
  local tgz="$1" proj="$2" top=""
  if [ "$proj" = "openssl" ]; then
    top="$(tar -tzf "$tgz" 2>/dev/null | grep -E '^openssl-[^/]+/Configure$' | head -n1 | cut -d/ -f1)"
    [ -n "$top" ] || top="$(tar -tzf "$tgz" 2>/dev/null | awk -F/ 'NF{print $1}' | grep '^openssl-' | grep -v '\.ctags' | head -n1)"
  else
    top="$(tar -tzf "$tgz" 2>/dev/null | grep -E '^openssh-[^/]+/configure$' | head -n1 | cut -d/ -f1)"
    [ -n "$top" ] || top="$(tar -tzf "$tgz" 2>/dev/null | awk -F/ 'NF{print $1}' | grep '^openssh-' | grep -v '\.ctags' | head -n1)"
  fi
  [ -n "$top" ] || { echo "无法识别压缩包顶层目录：$tgz"; exit 1; }
  printf "%s" "$top"
}

extract_and_cd_topdir(){ local tgz="$1" proj="$2" top; top="$(find_topdir "$tgz" "$proj")"; tar -xzf "$tgz"; cd "$top"; }

build_openssl(){
  cd "$BUILD_DIR"; download_checked "$OPENSSL_URL" "$OPENSSL_TGZ"; extract_and_cd_topdir "$OPENSSL_TGZ" "openssl"
  ./config --prefix="${PREFIX}" --openssldir="${OPENSSL_CONF_DIR}" shared zlib
  make -j"$(nproc)"; make install_sw install_man_docs || make install_sw
  echo "${PREFIX}/lib"    >  /etc/ld.so.conf.d/openssl-local.conf
  echo "${PREFIX}/lib64" >> /etc/ld.so.conf.d/openssl-local.conf || true
  ldconfig
  ln -sf "${PREFIX}/bin/openssl" /usr/bin/openssl
}

build_openssh(){
  cd "$BUILD_DIR"; download_checked "$OPENSSH_URL" "$OPENSSH_TGZ"; extract_and_cd_topdir "$OPENSSH_TGZ" "openssh"
  export PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig:${PREFIX}/lib64/pkgconfig:${PKG_CONFIG_PATH:-}"
  export CPPFLAGS="-I${PREFIX}/include ${CPPFLAGS:-}"
  export LDFLAGS="-L${PREFIX}/lib -Wl,-rpath,${PREFIX}/lib ${LDFLAGS:-}"
  # 关键：开启 Kerberos5 支持，避免 GSSAPIAuthentication 不被识别
  ./configure --prefix="${PREFIX}" --sysconfdir=/etc/ssh --with-pam --with-ssl-engine --with-kerberos5
  make -j"$(nproc)"; make install
  ln -sf "${PREFIX}/bin/ssh"   /usr/bin/ssh
  ln -sf "${PREFIX}/sbin/sshd" /usr/sbin/sshd
}

pick_free_port(){
  echo ">>> 在 ${RAND_MIN}-${RAND_MAX} 选择未占用端口..."
  for _ in $(seq 1 100); do
    PORT=$(( RANDOM % (RAND_MAX - RAND_MIN + 1) + RAND_MIN ))
    if ! ss -tuln | awk '{print $5}' | grep -q ":${PORT}\$"; then echo "选择端口：$PORT"; return 0; fi
  done
  echo "未找到空闲端口。"; exit 1
}

tune_sshd_config(){
  local cfg="/etc/ssh/sshd_config"
  [ -f "$cfg" ] || touch "$cfg"
  cp -a "$cfg" "${cfg}.$(date +%Y%m%d%H%M%S).bak"

  # 1) 注释旧/不支持的指令，避免校验失败
  sed -i -E 's/^[[:space:]]*(GSSAPICleanupCredentials|RSAAuthentication|RhostsRSAAuthentication)[[:space:]].*$/# &/g' "$cfg"

  # 如果当前 OpenSSH 未带 Kerberos5，就注释 GSSAPIAuthentication；如果带了，这行不会出错
  if ! /usr/local/sbin/sshd -T 2>/dev/null | grep -q 'gssapiauthentication'; then
    sed -i -E 's/^[[:space:]]*GSSAPIAuthentication[[:space:]].*$/# &/g' "$cfg"
  fi

  # 若有被粘连或自定义的 KexAlgorithms，回到默认更稳妥
  sed -i -E 's/^[[:space:]]*KexAlgorithms[[:space:]].*$/# &/g' "$cfg"

  # 2) 确保文件末尾有换行，避免 “sha256Port” 粘连
  sed -i -e '$a\' "$cfg"

  # 3) 注释旧 Port，插入新 Port（在首个 Match 之前插入，保证全局生效）
  sed -i -E 's/^[[:space:]]*Port[[:space:]].*$/# &/g' "$cfg"
  local ln
  ln=$(grep -nE '^[[:space:]]*Match[[:space:]]' "$cfg" | head -n1 | cut -d: -f1 || true)
  if [ -n "$ln" ]; then
    sed -i "${ln}i Port ${PORT}\n" "$cfg"
  else
    printf "\n# set by script\nPort %s\n" "$PORT" >> "$cfg"
  fi

  # 4) 确保 UsePAM yes
  if grep -qE '^[[:space:]]*UsePAM[[:space:]]+' "$cfg"; then
    sed -i -E 's/^[[:space:]]*#?[[:space:]]*UsePAM[[:space:]]+.*/UsePAM yes/g' "$cfg"
  else
    echo "UsePAM yes" >> "$cfg"
  fi
}

restart_sshd_safely(){
  echo ">>> 校验 sshd 配置..."
  if ! "${PREFIX}/sbin/sshd" -t -f /etc/ssh/sshd_config; then
    echo "sshd 配置校验失败，已保留备份：/etc/ssh/sshd_config.*.bak"; exit 1
  fi
  echo ">>> 重启 SSH 服务..."
  if systemctl list-unit-files | grep -q '^sshd\.service'; then
    systemctl restart sshd && systemctl enable sshd >/dev/null 2>&1 || true
  elif systemctl list-unit-files | grep -q '^ssh\.service'; then
    systemctl restart ssh && systemctl enable ssh >/dev/null 2>&1 || true
  else
    pkill -x sshd || true
    "${PREFIX}/sbin/sshd" -D & disown || true
  fi
}

print_results(){
  echo "============================================================"
  echo "OpenSSH 版本: $("${PREFIX}/bin/ssh" -V 2>&1 | head -n1)"
  echo "OpenSSL 版本: $("${PREFIX}/bin/openssl" version -a | head -n1)"
  echo "SSH 随机端口: ${PORT}"
  echo "连接示例：ssh -p ${PORT} <user>@<host>"
  echo "============================================================"
}

### 主流程 ###
need_root
detect_pm
sys_update
install_build_deps
prepare_dirs
# 若系统里已安装过新版本，可注释下一行两步以节省时间
build_openssl
build_openssh
pick_free_port
tune_sshd_config
restart_sshd_safely
print_results
